﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Hockey.Shared;

namespace Hockey.GameEngine
{
    /// <summary>
    /// Bond class implemetation
    /// </summary>
    internal class Bond : IBond
    {
        private bool _hasBeenVisited = false;
        private ePuckDir? _visitedByPlayer;
        private Node _node1;
        private Node _node2;

        /// <summary>
        /// Constructor - sets the nodes
        /// </summary>
        public Bond(Node node1, Node node2)
        {
            _node1 = node1;
            _node2 = node2;
            _hasBeenVisited = false;
        }

        /// <summary>
        /// Returns the flag whether the bond has been visited
        /// </summary>
        public bool HasBeenVisited
        {
            get { return _hasBeenVisited; }
        }

        /// <summary>
        /// Returns the visiting player
        /// </summary>
        public ePuckDir? VisitedByPlayer
        {
            get { return _visitedByPlayer; }
        }

        /// <summary>
        /// Returns the next node
        /// </summary>
        public INode GetNextNode(INode thisNode)
        {
            if (thisNode == _node1)
                return _node2;
            else if (thisNode == _node2)
                return _node1;
            else
            {
                Debug.Assert(false);
                throw new ApplicationException("Incorrect bond.");
            }
        }


        /// <summary>
        /// Makes the bond visited
        /// </summary>
        internal void MarkAsVisited(ePuckDir player)
        {
            _hasBeenVisited = true;
            _visitedByPlayer = player;
        }

        /// <summary>
        /// Marks the bond as unvisited - used for engine move searching
        /// </summary>
        internal void MarkAsUnvisited()
        {
            _hasBeenVisited = false;
            _visitedByPlayer = null;
        }
    }

    /// <summary>
    /// Node class implementation - node means a field the puck can be on
    /// </summary>
    internal class Node : INode
    {
        private Coordinate _coordinates;
        private bool _hasPuck = false;
        private bool _isGate = false;
        private bool _hasBeenVisited = false;
        private Dictionary<eMoveDirection, IBond> _bonds;

        /// <summary>
        /// Constructor
        /// </summary>
        public Node(Coordinate coordinates)
        {
            _bonds = new Dictionary<eMoveDirection, IBond>();
            _coordinates = coordinates;
        }

        #region Public API

        /// <summary>
        /// Returns the coordinate
        /// </summary>
        public Coordinate Coordinate
        {
            get
            {
                return _coordinates;
            }
        }
        
        /// <summary>
        /// Returns flag whether this node is get
        /// </summary>
        public bool IsGate
        {
            get { return _isGate; }
        }

        /// <summary>
        /// Returns flag whether the node has been visited
        /// </summary>
        public bool HasBeenVisited
        {
            get { return _hasBeenVisited; }
        }

        /// <summary>
        /// Returns the bonds
        /// </summary>
        public IDictionary<eMoveDirection, IBond> Bonds
        {
            get 
            {
                return _bonds;
            }
        }

        /// <summary>
        /// Returns flag whether this node has puck
        /// </summary>
        public bool HasPuck
        {
            get { return _hasPuck; }
        }

        #endregion

        #region Internal Methods for GameBoard
        
        /// <summary>
        /// Gains the puck
        /// </summary>
        internal void GainPuck()
        {
            _hasPuck = true;
        }

        /// <summary>
        /// Removes puck
        /// </summary>
        internal void RemovePuck()
        {
            _hasPuck = false;
        }

        /// <summary>
        /// Makes the gate from this node
        /// </summary>
        internal void MarkAsGate()
        {
            _isGate = true;
        }

        /// <summary>
        /// Marks that the node has been visited by the puck
        /// </summary>
        internal void MarkAsVisited()
        {
            _hasBeenVisited = true;
        }

        /// <summary>
        /// Unmark field as visited, used for engine
        /// </summary>
        internal void UnmarkAsVisited()
        {
            _hasBeenVisited = false;
        }

        /// <summary>
        /// Assings the bond with the node
        /// </summary>
        internal void AssignBond(eMoveDirection direction, Bond bond)
        {
            _bonds[direction] = bond;
        }

        #endregion

    }


    /// <summary>
    /// Stack of move steps used by engine for searching
    /// </summary>
    internal class MoveStepStack : Stack<eMoveDirection>
    {
        private bool _isFinal;

        /// <summary>
        /// Constructor
        /// </summary>
        public MoveStepStack()
            : base()
        {
            _isFinal = false;
        }

        /// <summary>
        /// Returns whether the steps in the stack form the complete move of the player
        /// </summary>
        public bool IsFinal
        {
            get
            {
                return _isFinal;
            }
        }

        /// <summary>
        /// Pushing method override
        /// </summary>
        /// <param name="direction"></param>
        public new void Push(eMoveDirection direction)
        {
            this.Push(direction, false);
        }

        /// <summary>
        /// Pushing method
        /// </summary>
        /// <param name="direction"></param>
        public void Push(eMoveDirection direction, bool isFinal)
        {
            base.Push(direction);
            _isFinal = isFinal;
        }

        /// <summary>
        /// Popping method
        /// </summary>
        /// <returns></returns>
        public new eMoveDirection Pop()
        {
            if (base.Count == 0)
                throw new ApplicationException("Empty MoveStepStack. Cannot Pop.");
            _isFinal = false;

            return base.Pop();
        }
    }

}
